import sys
import pandas as pd
import tkinter as tk
from tkinter import ttk, filedialog, messagebox
from tkinter.scrolledtext import ScrolledText
import os
import json
import akshare as ak
import datetime
from threading import Thread
import threading
import numpy as np
from PIL import Image, ImageTk  
from pathlib import Path
import signal  
        
class ExcelBrowser:
    def __init__(self, master):
        self.master = master
        self.financial_data_dir = Path("financial_notes")
        self.financial_data_dir.mkdir(exist_ok=True)
        self.current_show_st = True  
        self.current_show_marked = True  
        master.title("鹰眼")
        master.geometry("1400x800") 
        self.fullscreen_state = False
        self.font_style = ('Microsoft YaHei', 12) 
        self.text_color = 'black'
        self.tree_row_height = 26  
        self.sort_states = {}
        self.current_sort_column = None
        self.current_df = None
        self.original_df = None  
        self.data_history = []  
        self.current_data_index = -1  
        self.favorite_stocks = set()
        self.marked_stocks = set()  
        self.favorite_file = "favorite_stocks.json"
        self.marked_file = "marked_stocks.json"  
        self.load_favorites()
        self.load_marked()  
        self.settings_file = "app_settings.json"
        self.current_price_range = (10, 50)
        self.current_change_range = (10, 11)
        self.current_volume_ratio_range = (1.5, 3.0)
        self.current_volume_range = (10000, 50000)
        self.current_turnover_rate_range = (3.0, 10.0)
        self.current_pe_range = (0, 30)
        self.current_pb_range = (1.0, 5.0)         
        self.current_amount_range = (5000, 50000) 
        self.current_market_cap_range = (1e9, 5e10)
        self.load_settings()

        self.filter_states = {
            'price': False,
            'change': False,
            'volume_ratio': False,
            'volume': False, 
            'turnover_rate': False,
            'pe': False, 
            'pb': False,  
            'amount': False,
            'market_cap': False
        }

        self._setup_style()
        self.create_widgets()
        self._bind_events()
        self._update_time() 
        self.help_dialog = None  
        self.context_menu = None 
        
        master.protocol("WM_DELETE_WINDOW", self.quit_app)
        signal.signal(signal.SIGINT, self.handle_signal)
        self.master.after(100, self.manual_refresh_data)
        
    def handle_signal(self, signum, frame):
        """处理SIGINT信号"""
        self.master.after(0, self.quit_app)

    def quit_app(self):
        """安全退出程序"""
        self.save_marked() 
        self.master.destroy()
        self.master.quit()  

    def load_marked(self):
        """加载标记股票"""
        try:
            if os.path.exists(self.marked_file):
                with open(self.marked_file, 'r', encoding='utf-8') as f:
                    self.marked_stocks = set(json.load(f))
        except Exception as e:
            self.log(f"加载标记股票失败: {str(e)}", "error")

    def save_marked(self):
        """保存标记股票"""
        try:
            with open(self.marked_file, 'w', encoding='utf-8') as f:
                json.dump(list(self.marked_stocks), f, ensure_ascii=False)
        except Exception as e:
            self.log(f"保存标记股票失败: {str(e)}", "error")
        
    def load_favorites(self):
        """加载自选股票"""
        try:
            if os.path.exists(self.favorite_file):
                with open(self.favorite_file, 'r', encoding='utf-8') as f:
                    self.favorite_stocks = set(json.load(f))
        except Exception as e:
            self.log(f"加载自选股票失败: {str(e)}", "error")

    def save_favorites(self):
        """保存自选股票"""
        try:
            with open(self.favorite_file, 'w', encoding='utf-8') as f:
                json.dump(list(self.favorite_stocks), f, ensure_ascii=False)
        except Exception as e:
            self.log(f"保存自选股票失败: {str(e)}", "error")

    def load_settings(self):
        self.current_show_st = True
        self.current_show_marked = True
        
        try:
            if os.path.exists(self.settings_file):
                with open(self.settings_file, 'r') as f:
                    settings = json.load(f)
                    self.current_price_range = tuple(settings.get('price_range', (10, 50)))
                    self.current_market_cap_range = tuple(settings.get('market_cap_range', (1e9, 5e10)))
                    self.current_volume_ratio_range = tuple(settings.get('volume_ratio_range', (1.5, 3.0)))
                    self.current_volume_range = tuple(settings.get('volume_range', (10000, 50000)))
                    self.current_turnover_rate_range = tuple(settings.get('turnover_rate_range', (3.0, 10.0)))
                    self.current_amount_range = tuple(settings.get('amount_range', (5000, 50000))) 
                    self.current_pe_range = tuple(settings.get('pe_range', (0, 30)))
                    self.current_pb_range = tuple(settings.get('pb_range', (1.0, 5.0)))  # 新增
                    self.current_change_range = tuple(settings.get('change_range', (10, 11)))         
        except Exception as e:
            self.current_show_st = True
            self.current_show_marked = True
            self.log(f"加载设置失败: {str(e)}", "error")

    def save_settings(self):
        settings = {
            'price_range': self.current_price_range,
            'market_cap_range': self.current_market_cap_range,
            'volume_ratio_range': self.current_volume_ratio_range,
            'volume_range': self.current_volume_range,
            'turnover_rate_range': self.current_turnover_rate_range,
            'amount_range': self.current_amount_range,  
            'pe_range': self.current_pe_range , 
            'pb_range': self.current_pb_range,  
            'change_range': self.current_change_range ,
            'show_st': self.current_show_st,  
            'show_marked': self.current_show_marked  
        }
        try:
            with open(self.settings_file, 'w') as f:
                json.dump(settings, f)
        except Exception as e:
            self.log(f"保存设置失败: {str(e)}", "error")

    def _setup_style(self):
        self.style = ttk.Style()
        self.style.configure('TButton', font=self.font_style, foreground=self.text_color)
        self.style.configure('Treeview', font=self.font_style, rowheight=self.tree_row_height, foreground=self.text_color)
        self.style.configure('Treeview.Heading', font=('Microsoft YaHei', 12, 'bold'), foreground=self.text_color)
        self.style.configure('Toggled.TButton', font=self.font_style, foreground=self.text_color, background='lightblue')
        self.style.configure('Marked.Treeview', foreground='red')  
        
    def create_widgets(self):
        control_frame = ttk.Frame(self.master)
        control_frame.pack(pady=3, fill=tk.X)

        # 左侧按钮容器
        left_buttons_frame = ttk.Frame(control_frame)
        left_buttons_frame.pack(side=tk.LEFT, fill=tk.X)

        # 最新价按钮
        self.price_btn = ttk.Button(
            left_buttons_frame, 
            text=f"最新价{self.current_price_range[0]}-{self.current_price_range[1]}"
        )
        self.price_btn.pack(side=tk.LEFT, padx=2)
        self.price_btn.bind('<Button-1>', self.on_price_click)
        self.price_btn.bind('<Double-Button-1>', self.on_price_double_click)

        self.change_btn = ttk.Button(
            left_buttons_frame,
            text=f"涨跌幅{self.current_change_range[0]}%-{self.current_change_range[1]}%"
        )
        self.change_btn.pack(side=tk.LEFT, padx=2)
        self.change_btn.bind('<Button-1>', self.on_change_click)
        self.change_btn.bind('<Double-Button-1>', self.on_change_double_click)
       
        self.volume_btn = ttk.Button(
            left_buttons_frame,
            text=f"成交量{self.current_volume_range[0]//10000}-{self.current_volume_range[1]//10000}万手"
        )
        self.volume_btn.pack(side=tk.LEFT, padx=2)
        self.volume_btn.bind('<Button-1>', self.on_volume_click)
        self.volume_btn.bind('<Double-Button-1>', self.on_volume_double_click)

        #成交额按钮
        self.amount_btn = ttk.Button(
            left_buttons_frame,
            text=f"成交额{self.current_amount_range[0]}-{self.current_amount_range[1]}万"
        )
        self.amount_btn.pack(side=tk.LEFT, padx=2)
        self.amount_btn.bind('<Button-1>', self.on_amount_click)
        self.amount_btn.bind('<Double-Button-1>', self.on_amount_double_click)
        
         # 换手率按钮
        self.turnover_rate_btn = ttk.Button(
            left_buttons_frame,
            text=f"换手率{self.current_turnover_rate_range[0]}%-{self.current_turnover_rate_range[1]}%"
        )
        self.turnover_rate_btn.pack(side=tk.LEFT, padx=2)
        self.turnover_rate_btn.bind('<Button-1>', self.on_turnover_rate_click)
        self.turnover_rate_btn.bind('<Double-Button-1>', self.on_turnover_rate_double_click)
        
        # 量比按钮
        self.volume_ratio_btn = ttk.Button(
            left_buttons_frame,
            text=f"量比{self.current_volume_ratio_range[0]}-{self.current_volume_ratio_range[1]}"
        )
        self.volume_ratio_btn.pack(side=tk.LEFT, padx=2)
        self.volume_ratio_btn.bind('<Button-1>', self.on_volume_ratio_click)
        self.volume_ratio_btn.bind('<Double-Button-1>', self.on_volume_ratio_double_click)

        # 市盈率-动态按钮
        self.pe_btn = ttk.Button(
            left_buttons_frame,
            text=f"市盈率-动态{self.current_pe_range[0]}-{self.current_pe_range[1]}"
        )
        self.pe_btn.pack(side=tk.LEFT, padx=2)
        self.pe_btn.bind('<Button-1>', self.on_pe_click)
        self.pe_btn.bind('<Double-Button-1>', self.on_pe_double_click)
        
        # 市净率按钮（新增）
        self.pb_btn = ttk.Button(
            left_buttons_frame,
            text=f"市净率{self.current_pb_range[0]}-{self.current_pb_range[1]}"
        )
        self.pb_btn.pack(side=tk.LEFT, padx=2)
        self.pb_btn.bind('<Button-1>', self.on_pb_click)
        self.pb_btn.bind('<Double-Button-1>', self.on_pb_double_click)
        
         # 总市值按钮
        self.market_cap_btn = ttk.Button(
            left_buttons_frame,
            text=f"总市值{self.current_market_cap_range[0]:,.0f}-{self.current_market_cap_range[1]:,.0f}"
        )
        self.market_cap_btn.pack(side=tk.LEFT, padx=2)
        self.market_cap_btn.bind('<Button-1>', self.on_market_cap_click)
        self.market_cap_btn.bind('<Double-Button-1>', self.on_market_cap_double_click)
        
        
        self.st_var = tk.BooleanVar(value=True)
        st_check = ttk.Checkbutton(
            left_buttons_frame, 
            text="显示ST/*ST股票",
            variable=self.st_var,
            command=lambda: self.toggle_setting('st')
        )
        st_check.pack(side=tk.LEFT, padx=8)

        self.marked_var = tk.BooleanVar(value=True)
        marked_check = ttk.Checkbutton(
            left_buttons_frame, 
            text="显示标记股票",
            variable=self.marked_var,
            command=lambda: self.toggle_setting('marked')
        )
        marked_check.pack(side=tk.LEFT, padx=2)
        # ================== END ==================

        # 右侧状态和时间显示
        right_frame = ttk.Frame(control_frame)
        right_frame.pack(side=tk.RIGHT, fill=tk.X)
        
        # 时间标签
        self.time_label = ttk.Label(
            right_frame, 
            font=self.font_style,
            foreground=self.text_color
        )
        self.time_label.pack(side=tk.RIGHT, padx=10)

        # 状态标签
        self.status_label = ttk.Label(right_frame, text="")
        self.status_label.pack(side=tk.RIGHT, padx=10)
        self.log_area = ScrolledText(self.master, height=2, font=self.font_style, foreground=self.text_color)
        self.log_area.pack(pady=2, fill=tk.X)
        self.log_area.configure(state=tk.DISABLED)
        
        self.log_area.tag_config("success", foreground='green')
        self.log_area.tag_config("warning", foreground='orange')
        self.log_area.tag_config("error", foreground='red')
        self.log_area.tag_config("info", foreground='blue')
        self.log_area.tag_config("stat", foreground='purple')
        self.log_area.tag_config("up", foreground='red')
        self.log_area.tag_config("flat", foreground='gray')
        self.log_area.tag_config("down", foreground='green')
        
        # 创建树形视图和滚动条的容器
        tree_frame = ttk.Frame(self.master)
        tree_frame.pack(fill=tk.BOTH, expand=True)

        # 创建树形视图
        self.tree = ttk.Treeview(tree_frame, show="headings", columns=("Index",), selectmode="browse")
        self.tree.heading("Index", text="序号", command=lambda: self.sort_treeview("Index"))
        self.tree.column("Index", width=60, anchor='center')
        self.tree.tag_configure('marked', foreground='red')

        # 创建垂直滚动条
        vsb = ttk.Scrollbar(tree_frame, orient="vertical", command=self.tree.yview)
        vsb.pack(side=tk.RIGHT, fill=tk.Y)

        # 创建水平滚动条
        hsb = ttk.Scrollbar(self.master, orient="horizontal", command=self.tree.xview)
        hsb.pack(side=tk.BOTTOM, fill=tk.X)

        # 配置树形视图的滚动
        self.tree.configure(yscrollcommand=vsb.set, xscrollcommand=hsb.set)
        self.tree.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
    # 设置对话框方法
    def show_display_settings(self):
        dialog = tk.Toplevel(self.master)
        dialog.title("")
        dialog.geometry("300x150")
    
        # ST显示选项
        self.st_var = tk.BooleanVar(value=self.current_show_st)
        st_check = ttk.Checkbutton(
            dialog, 
            text="显示ST/*ST股票",
            variable=self.st_var,
            command=lambda: self.toggle_setting('st')
        )   
        st_check.pack(pady=5, anchor='w')
    
        # 标记显示选项
        self.marked_var = tk.BooleanVar(value=self.current_show_marked)
        marked_check = ttk.Checkbutton(
            dialog, 
            text="显示标记股票",
            variable=self.marked_var,
            command=lambda: self.toggle_setting('marked')
        )
        marked_check.pack(pady=5, anchor='w')

    def toggle_setting(self, setting_type):
        """切换设置并立即生效"""
        if setting_type == 'st':
            self.current_show_st = self.st_var.get()
        elif setting_type == 'marked':
            self.current_show_marked = self.marked_var.get()
    
        self.save_settings()
        self.apply_all_filters()

    def _bind_events(self):
        self.master.bind('<F11>', self.toggle_fullscreen)
        self.master.bind('<Escape>', lambda e: self.master.attributes('-fullscreen', False))
        self.master.bind('<Control-o>', self.load_and_show_file)
        self.master.bind('<Control-r>', self.manual_refresh_data)
        self.master.bind('<Control-s>', self.save_current_data)
        self.master.bind('<Control-f>', self.open_search_dialog)
        self.master.bind('<Control-i>', self.show_favorites)
        self.master.bind('<Control-z>', self.undo_last_display)  # Ctrl+Z快捷键
        self.master.bind('<Control-c>', self.copy_selected_row)
        self.master.bind('<Control-d>',self.copy_selected_names)
        self.tree.bind('<Up>', lambda e: self.tree.yview_scroll(-1, "units"))
        self.tree.bind('<Down>', lambda e: self.tree.yview_scroll(1, "units"))
        self.tree.bind('<Button-3>', self.show_context_menu)  # 右键菜单
        self.tree.bind('<Button-1>', self.on_tree_click)  # 左键点击
        self.master.bind('<Control-a>', self.select_all_rows)  # 全选绑定
        self.master.bind('<Control-h>', self.show_help)  # 添加Ctrl+H绑定
        self.tree.bind('<Double-Button-1>', self.open_stock_info)
        self.master.bind('<Control-b>', self.show_marked_stocks)  # Ctrl+B绑定
        
    def generate_stock_url(self, stock_code):
        """生成股票信息URL（以新浪财经为例）"""
        code = stock_code[2:]  # 去除市场前缀（sh/sz）
        return f"https://finance.sina.com.cn/realstock/company/{stock_code}/nc.shtml"
    
    def open_stock_info(self, event):
        item = self.tree.identify_row(event.y)
        if item:
            try:
                stock_code = self.tree.item(item)['values'][1]  
                url = self.generate_stock_url(stock_code)
                import webbrowser
                webbrowser.open(url)
            except Exception as e:
                self.log(f"跳转失败：{str(e)}", "error")
    def manual_refresh_data(self, event=None):
        Thread(target=self._update_data).start()

    def _update_data(self):
        try:
            self.master.after(0, lambda: self.log("获取A股数据...", "info"))
            
            # 获取A股实时数据
            try:
                df = ak.stock_zh_a_spot_em()
                if df is None or df.empty:
                    self.master.after(0, lambda: self.log("akshare返回数据为空，可能是非交易时段或网络问题", "error"))
                    return
                # 添加市盈率列
                df['市盈率'] = df['市盈率-动态']
                df['市净率'] = df['市净率']  
                self.master.after(0, lambda: self.log(f"成功获取数据，共 {len(df)} 条", "info"))
            except Exception as e:
                self.master.after(0, lambda: self.log(f"获取基础数据失败: {str(e)}", "error"))
                return

            # 使用正确的列名映射
            rename_dict = {
                '代码': '代码',
                '名称': '名称',
                '最新价': '最新价',
                '涨跌幅': '涨跌幅',
                '成交量': '成交量',
                '成交额': '成交额',
                '换手率': '换手率',
                '量比': '量比',
                '市盈率-动态': '市盈率-动态',
                '市净率': '市净率',  
                '总市值': '总市值'
            }
            
            # 检查所需列是否存在
            missing_cols = [col for col in rename_dict.keys() if col not in df.columns]
            if missing_cols:
                self.master.after(0, lambda: self.log(f"缺少以下列: {', '.join(missing_cols)}", "warning"))
            
            # 只选择需要的列
            available_cols = [col for col in rename_dict.keys() if col in df.columns]
            df_final = df[available_cols].copy()
            
            # 数据类型转换（在重命名列之前进行）
            try:
                # 处理百分比列
                df_final['涨跌幅'] = pd.to_numeric(df_final['涨跌幅'], errors='coerce')
                df_final['换手率'] = pd.to_numeric(df_final['换手率'], errors='coerce')
                df_final['市盈率'] = pd.to_numeric(df_final['市盈率'], errors='coerce')
                
                # 处理其他数值列
                df_final['最新价'] = pd.to_numeric(df_final['最新价'], errors='coerce')
                df_final['成交量'] = pd.to_numeric(df_final['成交量'], errors='coerce')
                df_final['成交额'] = pd.to_numeric(df_final['成交额'], errors='coerce')
                df_final['量比'] = pd.to_numeric(df_final['量比'], errors='coerce')
                df_final['总市值'] = pd.to_numeric(df_final['总市值'], errors='coerce')
                
            except Exception as e:
                self.master.after(0, lambda e=e: self.log(f"数据类型转换出错: {str(e)}", "error"))
            
            # 重命名列
            df_final.columns = [rename_dict[col] for col in available_cols]
            
            # 添加股票代码前缀
            df_final['代码'] = df_final['代码'].astype(str).apply(
                lambda x: f"sh{x}" if str(x).startswith(('6', '68', '7')) else f"sz{x}" if str(x).startswith(('0', '3')) else f"bj{x}"
            )
            
            # 更新数据
            self.original_df = df_final.copy()
            self.current_df = df_final.copy()
            self.add_to_history(self.current_df)  
            self.master.after(0, self.show_data)
            self.master.after(0, lambda: self.log(f"{len(df_final)}"))
            
            # 重置筛选状态
            self.filter_states = {key: False for key in self.filter_states}
            # 重置按钮样式
            self.price_btn.configure(style='TButton')
            self.market_cap_btn.configure(style='TButton')
            self.volume_ratio_btn.configure(style='TButton')
            self.turnover_rate_btn.configure(style='TButton')
            self.amount_btn.configure(style='TButton')
            self.pe_btn.configure(style='TButton')
            
        except Exception as e:
            self.master.after(0, lambda: self.log(f"获取失败: {str(e)}", "error"))
            import traceback
            self.master.after(0, lambda: self.log(f"错误详情: {traceback.format_exc()}", "error"))
    def get_market_data(self):
        try:
            df = ak.stock_zh_a_spot_em()
            df['带前缀代码'] = df['代码'].apply(self.add_market_prefix)
            
            selected_columns = {
                '带前缀代码': '代码',
                '名称': '名称',
                '最新价': '最新价',
                '涨跌幅': '涨跌幅',
                '成交量': '成交量',
                '成交额': '成交额',
                '换手率': '换手率',
                '量比': '量比',
                '市盈率-动态': '市盈率-动态',
                '市净率': '市净率',
                '总市值': '总市值'
            }
            
            df = df[list(selected_columns.keys())].rename(columns=selected_columns)
            numeric_cols = ['最新价', '涨跌幅', '成交量','成交额','换手率', '量比','市盈率-动态','市净率','总市值']
            df[numeric_cols] = df[numeric_cols].apply(pd.to_numeric, errors='coerce')
            
            return df.dropna()
        except Exception as e:
            self.log(f"数据获取失败: {str(e)}", "error")
            return None

    def load_and_show_file(self, event=None):
        file = filedialog.askopenfilename(title="选择Excel文件",filetypes=[("Excel文件", "*.xlsx *.xls")])
        if not file: return
        
        try:
            df = pd.read_excel(file)
            df.columns = df.columns.str.strip()
            column_mapping = {
                '代码': '代码',
                '名称': '名称',
                '最新价': '最新价',
                '涨跌幅': '涨跌幅',
                '成交量': '成交量',
                '成交额': '成交额',
                '换手率': '换手率',
                '量比': '量比',
                '市盈率-动态': '市盈率-动态',
                '市净率':'市净率',
                '总市值': '总市值'
            }
            df.rename(columns=column_mapping, inplace=True)
            
            required_columns = {'代码', '名称', '最新价', '涨跌幅','成交量','成交额','换手率', '量比','市盈率-动态', '总市值'}
            missing_cols = required_columns - set(df.columns)
            if missing_cols:
                self.log(f"文件 {os.path.basename(file)} 缺少必要列: {missing_cols}，已跳过", "error")
                return 
                
            numeric_cols = ['最新价', '涨跌幅', '成交量','成交额' ,'换手率', '量比', '市盈率-动态','总市值']
            df[numeric_cols] = df[numeric_cols].apply(pd.to_numeric, errors='coerce')
            
            self.original_df = df.copy()
            self.current_df = df.copy()
            self.add_to_history(self.current_df)  
            self.show_data()
            self.log(f"已加载并显示: {os.path.basename(file)} ({len(df)}行)", "success")
        except Exception as e:
            self.log(f"加载失败: {os.path.basename(file)} - {str(e)}", "error")

    def save_current_data(self, event=None):
        if self.current_df is not None and not self.current_df.empty:
            filename = f"{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}_筛选股票数据.xlsx"
            try:
                self.current_df.to_excel(filename, index=False)
                self.log(f"数据已保存至: {filename}", "success")
                self.status_label.config(text=f"最后保存: {filename}")
            except Exception as e:
                self.log(f"保存失败: {str(e)}", "error")
        else:
            self.log("没有可保存的数据", "warning")

    def add_market_prefix(self, code):
        code_str = str(code)
        if code_str.startswith('6'):
            return f'sh{code_str}'
        elif code_str.startswith(('0', '3')):
            return f'sz{code_str}'
        elif code_str.startswith(('4', '8')):
            return f'bj{code_str}'
        return code_str

    def show_data(self):
        self.tree.delete(*self.tree.get_children())
        if self.current_df is not None and not self.current_df.empty:
        
            self.load_marked()
            columns = ["Index", "代码", "名称", "最新价", "涨跌幅", "成交量", 
                      "成交额", "换手率", "量比", "市盈率-动态","市净率", "总市值"]
            
            existing_columns = ["Index"] + list(self.current_df.columns)
            display_columns = [col for col in columns if col in existing_columns]
            
            self.tree["columns"] = display_columns
            
            for col in display_columns:
                if col == "Index":
                    self.tree.heading(col, text="序号", command=lambda c=col: self.sort_treeview(c))
                    self.tree.column(col, width=60, anchor='center')
                else:
                    self.tree.heading(col, text=col, command=lambda c=col: self.sort_treeview(c))
                    self.tree.column(col, width=self.get_column_width(col), anchor='w')

            for idx, row in enumerate(self.current_df.iterrows(), start=1):
                row_data = row[1]
                values = [idx]
                tags = []
                # 检查是否为标记股票
                if row_data['代码'] in self.marked_stocks:
                    tags.append('marked')
                for col in display_columns[1:]:
                    values.append(row_data.get(col, ""))
                self.tree.insert("", tk.END, values=values, tags=tags)

        self.show_statistics()

    def get_column_width(self, col):
        widths = {
            "代码": 100,
            "名称": 100,
            "最新价": 80,
            "涨跌幅": 80,
            "成交量": 100,
            "成交额": 120,
            "换手率": 80,
            "量比": 70,
            "总市值": 120,
            "市盈率-动态": 80,
            "市净率":80,
            "Index": 60
        }
        return widths.get(col, 100)

    def reset_display(self, event=None):
        if hasattr(self, 'search_dialog') and self.search_dialog.winfo_exists():
            self.search_dialog.destroy()
        if self.current_df is not None:
            self._refresh_display()
            self.log("已恢复显示全部数据", "info")

    def open_search_dialog(self, event=None):
        self.search_dialog = tk.Toplevel(self.master)
        self.search_dialog.title("")
    
        # 移除所有窗口装饰（包含标题栏）
        self.search_dialog.overrideredirect(True)
    
        # 设置窗口位置（居中显示）
        main_x = self.master.winfo_x()
        main_y = self.master.winfo_y()
        main_width = self.master.winfo_width()
        dialog_width = 300
        dialog_height = 100
        x = main_x + (main_width - dialog_width) // 2
        y = main_y + 100 
    
        self.search_dialog.geometry(f"{dialog_width}x{dialog_height}+{x}+{y}")
    
        # 创建容器Frame
        container = ttk.Frame(self.search_dialog)
        container.pack(padx=10, pady=10, fill=tk.BOTH, expand=True)
    
        # 输入框部分
        input_frame = ttk.Frame(container)
        input_frame.pack(fill=tk.X)
    
        ttk.Label(input_frame, text="输入搜索关键词:").pack(side=tk.LEFT)
        self.search_entry = ttk.Entry(input_frame)
        self.search_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=5)
        self.search_entry.focus_set()
    
        # 按钮部分
        button_frame = ttk.Frame(container)
        button_frame.pack(pady=5)
    
        ttk.Button(button_frame, text="搜索", 
                command=self.perform_search).pack(side=tk.LEFT, padx=5)
        ttk.Button(button_frame, text="关闭", 
                command=self.search_dialog.destroy).pack(side=tk.RIGHT, padx=5)
    
        # 绑定回车键
        self.search_entry.bind('<Return>', lambda e: self.perform_search())
        # 获取焦点
        self.search_dialog.grab_set()
        
    def perform_search(self, event=None):
        keyword = self.search_entry.get().strip().lower()
        if not keyword:
            messagebox.showwarning("警告", "请输入搜索关键词")
            return
        if self.current_df is None:
            messagebox.showwarning("警告", "请先加载数据")
            return
        self.add_to_history(self.current_df.copy())  
        search_df = self.current_df.copy()
        mask = (search_df['代码'].astype(str).str.lower().str.contains(keyword) | 
            search_df['名称'].astype(str).str.lower().str.contains(keyword))
    
        if mask.any():
            filtered_df = search_df[mask].copy()
             
            self.current_df = filtered_df         
            self.show_data()
            self.search_dialog.destroy()
        else:
            messagebox.showinfo("提示", "未找到匹配结果")
        self.add_to_history(self.current_df.copy())  
        
    def toggle_fullscreen(self, event=None):
        if not self.fullscreen_state:
            # 如果不是全屏，先最大化再全屏
            #self.master.state('zoomed')  # Windows下最大化窗口
            self.master.update()  # 确保最大化完成
            self.master.attributes('-fullscreen', True)
        else:
            # 如果是全屏，直接退出全屏
            self.master.attributes('-fullscreen', False)
            
        self.fullscreen_state = not self.fullscreen_state
        return "break"

    def log(self, message, tag=None):
        self.log_area.configure(state=tk.NORMAL)
        self.log_area.insert(tk.END, message + "\n", tag)
        self.log_area.configure(state=tk.DISABLED)
        self.log_area.see(tk.END)

    def sort_treeview(self, col):
        """排序功能"""
        if self.current_df is None or self.current_df.empty:
            return
            
        current_state = self.sort_states.get(col, None)
        new_order = not current_state if col == self.current_sort_column else True
        
        try:
            if col == "Index":
                sorted_df = self.current_df.iloc[::-1] if not new_order else self.current_df
            else:
                sorted_df = self.current_df.sort_values(
                    by=col, 
                    ascending=new_order,
                    key=lambda x: x.astype(str).str.lower() if x.dtype == "object" else x
                )
            self.current_df = sorted_df.reset_index(drop=True)
            self.show_data()
            
            # 更新排序状态
            self.sort_states[col] = new_order
            self.current_sort_column = col
            self._update_sort_indicator(col, new_order)
            
        except Exception as e:
            messagebox.showerror("排序错误", f"排序失败：{str(e)}")

    def _update_sort_indicator(self, col, ascending):
        indicator = " ↑" if ascending else " ↓"
        for column in self.tree["columns"]:
            current_text = self.tree.heading(column)["text"]
            if column == col:
                new_text = current_text.split(" ↑")[0].split(" ↓")[0] + indicator
            else:
                new_text = current_text.split(" ↑")[0].split(" ↓")[0]
            self.tree.heading(column, text=new_text)

    def _refresh_display(self):
        self.show_data()

    def apply_all_filters(self):
        """应用所有激活的筛选条件"""
        self.current_df = self.original_df.copy()
        if not self.current_show_st:
            self.current_df = self.current_df[
                ~self.current_df['名称'].str.startswith(('ST', '*ST'),na=False)
            ]   
    
        # 过滤条件：标记股票
        if not self.current_show_marked:
            self.current_df = self.current_df[
                ~self.current_df['代码'].isin(self.marked_stocks)
            ]
        # 记录激活的筛选条件
        active_filters = []
        
        # 应用最新价筛选
        if self.filter_states['price']:
            min_val, max_val = self.current_price_range
            mask = (self.current_df['最新价'] >= min_val) & (self.current_df['最新价'] <= max_val)
            self.current_df = self.current_df[mask]
            active_filters.append(f"最新价{min_val}-{max_val}")
        
        if self.filter_states['change']:
            min_val, max_val = self.current_change_range
            mask = (self.current_df['涨跌幅'] >= min_val) & (self.current_df['涨跌幅'] <= max_val)
            self.current_df = self.current_df[mask]
            active_filters.append(f"涨跌幅{min_val}%-{max_val}%")
        
        # 应用总市值筛选
        if self.filter_states['market_cap']:
            min_val, max_val = self.current_market_cap_range
            mask = (self.current_df['总市值'] >= min_val) & (self.current_df['总市值'] <= max_val)
            self.current_df = self.current_df[mask]
            active_filters.append(f"总市值{min_val:,.0f}-{max_val:,.0f}")
        
        # 应用量比筛选
        if self.filter_states['volume_ratio']:
            min_val, max_val = self.current_volume_ratio_range
            mask = (self.current_df['量比'] >= min_val) & (self.current_df['量比'] <= max_val)
            self.current_df = self.current_df[mask]
            active_filters.append(f"量比{min_val}-{max_val}")
        
        if self.filter_states['volume']:
            min_val, max_val = self.current_volume_range
            mask = (self.current_df['成交量'] >= min_val) & (self.current_df['成交量'] <= max_val)
            self.current_df = self.current_df[mask]
            active_filters.append(f"成交量{min_val//10000}-{max_val//10000}万手")
        
        # 应用换手率筛选
        if self.filter_states['turnover_rate']:
            min_val, max_val = self.current_turnover_rate_range
            mask = (self.current_df['换手率'] >= min_val) & (self.current_df['换手率'] <= max_val)
            self.current_df = self.current_df[mask]
            active_filters.append(f"换手率{min_val}%-{max_val}%")
        
        # 应用成交额筛选
        if self.filter_states['amount']:
            min_val, max_val = self.current_amount_range
            mask = (self.current_df['成交额'] >= min_val * 1e4) & (self.current_df['成交额'] <= max_val * 1e4)
            self.current_df = self.current_df[mask]
            active_filters.append(f"成交额{min_val}-{max_val}万")
        
        # 应用市盈率-动态筛选
        if self.filter_states['pe']:
            min_val, max_val = self.current_pe_range
            mask = (self.current_df['市盈率-动态'] >= min_val) & (self.current_df['市盈率-动态'] <= max_val)
            self.current_df = self.current_df[mask]
            active_filters.append(f"市盈率-动态{min_val}-{max_val}")
            
        if self.filter_states['pb']:
            min_val, max_val = self.current_pb_range
            mask = (self.current_df['市净率'] >= min_val) & (self.current_df['市净率'] <= max_val)
            self.current_df = self.current_df[mask]
            active_filters.append(f"市净率{min_val}-{max_val}")
            
        self.add_to_history(self.current_df)
        self.show_data()
        
        # 显示筛选信息
        if active_filters:
            self.log(f"{len(self.current_df)}", "info")
        else:
            self.log(f"{len(self.current_df)}", "info")

    def filter_by_price(self):
        """最新价筛选"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        # 切换状态
        self.filter_states['price'] = not self.filter_states['price']
        self.price_btn.configure(style='Toggled.TButton' if self.filter_states['price'] else 'TButton')
        
        # 应用所有筛选条件
        self.apply_all_filters()

    def filter_by_change(self):
        """涨跌幅筛选"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        # 切换状态
        self.filter_states['change'] = not self.filter_states['change']
        self.change_btn.configure(style='Toggled.TButton' if self.filter_states['change'] else 'TButton')
        
        # 应用所有筛选条件
        self.apply_all_filters()
    def filter_by_market_cap(self):
        """总市值筛选"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        # 切换状态
        self.filter_states['market_cap'] = not self.filter_states['market_cap']
        self.market_cap_btn.configure(style='Toggled.TButton' if self.filter_states['market_cap'] else 'TButton')
        
        # 应用所有筛选条件
        self.apply_all_filters()

    def filter_by_volume_ratio(self):
        """量比筛选"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        # 切换状态
        self.filter_states['volume_ratio'] = not self.filter_states['volume_ratio']
        self.volume_ratio_btn.configure(style='Toggled.TButton' if self.filter_states['volume_ratio'] else 'TButton')
        
        # 应用所有筛选条件
        self.apply_all_filters()

    def filter_by_turnover_rate(self):
        """换手率筛选"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        # 切换状态
        self.filter_states['turnover_rate'] = not self.filter_states['turnover_rate']
        self.turnover_rate_btn.configure(style='Toggled.TButton' if self.filter_states['turnover_rate'] else 'TButton')
        
        # 应用所有筛选条件
        self.apply_all_filters()

    def filter_by_amount(self):
        """成交额筛选"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        # 切换状态
        self.filter_states['amount'] = not self.filter_states['amount']
        self.amount_btn.configure(style='Toggled.TButton' if self.filter_states['amount'] else 'TButton')
        
        # 应用所有筛选条件
        self.apply_all_filters()

    def filter_by_pe(self):
        """市盈率-动态筛选"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        # 切换状态
        self.filter_states['pe'] = not self.filter_states['pe']
        self.pe_btn.configure(style='Toggled.TButton' if self.filter_states['pe'] else 'TButton')
        
        # 应用所有筛选条件
        self.apply_all_filters()
    def on_price_click(self, event):
        """处理最新价按钮单击事件"""
        # 使用after_cancel来取消之前的双击检测
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        # 延迟执行单击操作，给双击检测留出时间
        self._after_id = self.master.after(300, self.filter_by_price)
    def on_change_click(self, event):
        """处理涨跌幅按钮单击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        self._after_id = self.master.after(300, self.filter_by_change)
    def on_market_cap_click(self, event):
        """处理总市值按钮单击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        self._after_id = self.master.after(300, self.filter_by_market_cap)

    def on_volume_ratio_click(self, event):
        """处理量比按钮单击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        self._after_id = self.master.after(300, self.filter_by_volume_ratio)

    def on_volume_click(self, event):
        """处理成交量按钮单击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        self._after_id = self.master.after(300, self.filter_by_volume)

    def filter_by_volume(self):
        """成交量筛选"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        self.filter_states['volume'] = not self.filter_states['volume']
        self.volume_btn.configure(style='Toggled.TButton' if self.filter_states['volume'] else 'TButton')
        self.apply_all_filters()
    
    def on_volume_double_click(self, event):
        """处理成交量按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        dialog = tk.Toplevel(self.master)
        dialog.title("设置成交量区间")
        dialog.geometry("300x120")
        ttk.Label(dialog, text="输入成交量区间（万手，例如：1-5）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        # 显示转换为万手的数值
        entry.insert(0, f"{self.current_volume_range[0]//10000}-{self.current_volume_range[1]//10000}")
        entry.pack(pady=5)
        entry.focus_set()
        
        def save_range():
            input_val = entry.get().strip()
            try:
                min_val, max_val = map(float, input_val.split('-'))
                # 转换为手的单位（1万手=10000手）
                min_val = int(min_val * 10000)
                max_val = int(max_val * 10000)
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_volume_range = (min_val, max_val)
                self.volume_btn.config(text=f"成交量{min_val//10000}-{max_val//10000}万手")
                self.save_settings()
                if self.filter_states['volume']:
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：1-5")
        
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()
    
    def on_pe_click(self, event):
        """处理市盈率-动态单击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        self._after_id = self.master.after(300, self.filter_by_pe)
    def on_pb_click(self, event):
        """处理市净率单击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id    
        self._after_id = self.master.after(300, self.filter_by_pb)

    def on_pb_double_click(self, event):
        """处理市净率双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        dialog = tk.Toplevel(self.master)
        dialog.title("设置市净率区间")
        dialog.geometry("300x120")
        ttk.Label(dialog, text="输入市净率区间（例如：1.0-5.0）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_pb_range[0]}-{self.current_pb_range[1]}")
        entry.pack(pady=5)
        entry.focus_set()
        
        def save_range():
            try:
                min_val, max_val = map(float, entry.get().split('-'))
                if min_val > max_val: 
                    min_val, max_val = max_val, min_val
                self.current_pb_range = (min_val, max_val)
                self.pb_btn.config(text=f"市净率{min_val}-{max_val}")
                self.save_settings()
                if self.filter_states['pb']: 
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：1.0-5.0")
        
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()

    def filter_by_pb(self):
        """市净率筛选逻辑"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        self.filter_states['pb'] = not self.filter_states['pb']
        self.pb_btn.configure(style='Toggled.TButton' if self.filter_states['pb'] else 'TButton')
        self.apply_all_filters()
        
    def on_turnover_rate_click(self, event):
        """处理换手率按钮单击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        self._after_id = self.master.after(300, self.filter_by_turnover_rate)
    
    def on_price_double_click(self, event):
        """处理最新价按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
        
        dialog = tk.Toplevel(self.master)
        dialog.title("设置最新价区间")
        dialog.geometry("320x140")
        ttk.Label(dialog, text="输入最新价区间（例如：10-50）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_price_range[0]}-{self.current_price_range[1]}")
        entry.pack(pady=5)
        entry.focus_set()
    
        def save_range():
            input_val = entry.get().strip()
            try:
                min_val, max_val = map(float, input_val.split('-'))
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_price_range = (min_val, max_val)
                self.price_btn.config(text=f"最新价{min_val}-{max_val}")
                self.save_settings()
                if self.filter_states['price']:
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：10-50")
    
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()

    def on_change_double_click(self, event):
        """处理涨跌幅按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
        
        dialog = tk.Toplevel(self.master)
        dialog.title("设置涨跌幅区间")
        dialog.geometry("320x140")
        ttk.Label(dialog, text="输入涨跌幅区间（例如：-10-10）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_change_range[0]}-{self.current_change_range[1]}")
        entry.pack(pady=5)
        entry.focus_set()

        def save_range():
            input_val = entry.get().strip()
            try:
                min_val, max_val = map(float, input_val.split('-'))
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_change_range = (min_val, max_val)
                self.change_btn.config(text=f"涨跌幅{min_val:+.2f}%-{max_val:+.2f}%")
                self.save_settings()
                if self.filter_states['change']:
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：-10-10")
    
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()
    def on_pe_double_click(self, event):
        """处理市盈率-动态按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
        
        dialog = tk.Toplevel(self.master)
        dialog.title("设置市盈率-动态区间")
        dialog.geometry("300x120")
        ttk.Label(dialog, text="输入市盈率-动态区间（例如：0-30）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_pe_range[0]:,.0f}-{self.current_pe_range[1]:,.0f}")
        entry.pack(pady=5)
        entry.focus_set()
    
        def save_range():
            input_val = entry.get().strip().replace(',', '')
            try:
                min_val, max_val = map(float, input_val.split('-'))
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_pe_range = (min_val, max_val)
                self.pe_btn.config(text=f"市盈率-动态{min_val:,.0f}-{max_val:,.0f}")
                self.save_settings()
                if self.filter_states['pe']:
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：0-30")
    
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()
        
    def on_market_cap_double_click(self, event):
        """处理总市值按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
        
        dialog = tk.Toplevel(self.master)
        dialog.title("设置总市值区间")
        dialog.geometry("300x120")
        ttk.Label(dialog, text="输入总市值区间（例如：1000000000-50000000000）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_market_cap_range[0]:,.0f}-{self.current_market_cap_range[1]:,.0f}")
        entry.pack(pady=5)
        entry.focus_set()
    
        def save_range():
            input_val = entry.get().strip().replace(',', '')
            try:
                min_val, max_val = map(float, input_val.split('-'))
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_market_cap_range = (min_val, max_val)
                self.market_cap_btn.config(text=f"总市值{min_val:,.0f}-{max_val:,.0f}")
                self.save_settings()
                if self.filter_states['market_cap']:
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：1000000000-50000000000")
    
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()

    def on_amount_double_click(self, event):
        """处理成交额按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id

        dialog = tk.Toplevel(self.master)
        dialog.title("设置成交额区间")
        dialog.geometry("300x120")
        ttk.Label(dialog, text="输入成交额区间（例如：100000-500000）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_amount_range[0]:,.0f}-{self.current_amount_range[1]:,.0f}")
        entry.pack(pady=5)
        entry.focus_set()
        def save_range():
            input_val = entry.get().strip().replace(',', '')
            try:
                min_val, max_val = map(float, input_val.split('-'))
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_amount_range = (min_val, max_val)
                self.amount_btn.config(text=f"成交额{min_val:,.0f}-{max_val:,.0f}万")
                self.save_settings()
                if self.filter_states['amount']:
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：100000-500000")
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()

    def on_volume_ratio_double_click(self, event):
        """处理量比按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        dialog = tk.Toplevel(self.master)
        dialog.title("设置量比区间")
        dialog.geometry("300x120")
        ttk.Label(dialog, text="输入量比区间（例如：1.5-3.0）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_volume_ratio_range[0]}-{self.current_volume_ratio_range[1]}")
        entry.pack(pady=5)
        entry.focus_set()
        
        def save_range():
            input_val = entry.get().strip()
            try:
                min_val, max_val = map(float, input_val.split('-'))
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_volume_ratio_range = (min_val, max_val)
                self.volume_ratio_btn.config(text=f"量比{min_val}-{max_val}")
                self.save_settings()
                if self.filter_states['volume_ratio']:
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：1.5-3.0")
        
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()

    def on_turnover_rate_double_click(self, event):
        """处理换手率按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        dialog = tk.Toplevel(self.master)
        dialog.title("设置换手率区间")
        dialog.geometry("300x120")
        ttk.Label(dialog, text="输入换手率区间（例如：3.0-10.0）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_turnover_rate_range[0]}-{self.current_turnover_rate_range[1]}")
        entry.pack(pady=5)
        entry.focus_set()
        
        def save_range():
            input_val = entry.get().strip()
            try:
                min_val, max_val = map(float, input_val.split('-'))
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_turnover_rate_range = (min_val, max_val)
                self.turnover_rate_btn.config(text=f"换手率{min_val}%-{max_val}%")
                self.save_settings()
                if self.filter_states['turnover_rate']:
                    self.apply_all_filters()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：3.0-10.0")
        
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()

    def _update_time(self):
        """更新时间显示"""
        weekday_map = {
            0: '一', 1: '二', 2: '三',
            3: '四', 4: '五', 5: '六', 6: '日'
        }
        current_time = datetime.datetime.now()
        weekday = weekday_map[current_time.weekday()]
        time_str = current_time.strftime(f'%Y-%m-%d %H:%M:%S 星期{weekday}')
        self.time_label.config(text=time_str)
        self.master.after(1000, self._update_time)  # 每秒更新一次

    def show_context_menu(self, event):
        """显示增强的右键菜单"""
        item = self.tree.identify_row(event.y)
        if not item:
            return
            
        self.tree.selection_set(item)
        selected_code = self.tree.item(item)['values'][1]
        
        # 销毁旧菜单
        if self.context_menu and self.context_menu.winfo_exists():
            self.context_menu.destroy()
            
        self.context_menu = tk.Menu(self.master, tearoff=0)
        
        if selected_code in self.marked_stocks:
            self.context_menu.add_command(label="取消标记", 
                                        command=lambda: self.toggle_mark(selected_code, False))
        else:
            self.context_menu.add_command(label="标记", 
                                        command=lambda: self.toggle_mark(selected_code, True))
    
        
        self.context_menu.add_command(label="备注", 
                                command=lambda: self.show_financial(selected_code))
        
        # 自选股菜单
        if selected_code in self.favorite_stocks:
            self.context_menu.add_command(label="删除自选", command=lambda: self.remove_from_favorites(selected_code))
        else:
            self.context_menu.add_command(label="加入自选", command=lambda: self.add_to_favorites(selected_code))
        
        self.context_menu.post(event.x_root, event.y_root)
        
        # 绑定左键关闭菜单
        self.master.bind('<Button-1>', self.close_context_menu)
        
    def close_context_menu(self, event):
        """左键点击关闭右键菜单"""
        if self.context_menu and self.context_menu.winfo_exists():
            self.context_menu.destroy()
        self.master.unbind('<Button-1>')

    def toggle_mark(self, stock_code, mark=True):
        """切换标记状态"""
        try:
            if mark:
                self.marked_stocks.add(stock_code)
            else: 
                self.marked_stocks.discard(stock_code)
        
            self.save_marked()
            self._refresh_display()
            action = "标记" if mark else "取消标记"
            self.log(f"已{action}股票：{stock_code}", "success")
        except Exception as e:
            self.log(f"标记操作失败: {str(e)}", "error")


    def show_financial(self, stock_code):
        """显示/编辑分析笔记"""
        dialog = tk.Toplevel(self.master)
        dialog.title(f"{stock_code} 笔记")
        dialog.geometry("600x400")
    
        # 创建文本编辑区域
        text_area = ScrolledText(dialog, wrap=tk.WORD, font=('Microsoft YaHei', 12))
        text_area.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
    
        # 加载已有内容
        file_path = self.financial_data_dir / f"{stock_code}.txt"
        try:
            if file_path.exists():
                with open(file_path, 'r', encoding='utf-8') as f:
                    content = f.read()
                    text_area.insert(tk.END, content)
        except Exception as e:
            self.log(f"加载财务笔记失败: {str(e)}", "error")
    
        # 保存按钮
        def save_content():
            try:
                with open(file_path, 'w', encoding='utf-8') as f:
                    content = text_area.get("1.0", tk.END).strip()
                    f.write(content)
                self.log(f"已保存 {stock_code} 的财务笔记", "success")
                messagebox.showinfo("保存成功", "内容已成功保存！", parent=dialog) 
            except Exception as e:
                self.log(f"保存失败: {str(e)}", "error")
                messagebox.showerror("保存失败", f"保存时发生错误：{str(e)}", parent=dialog)
        dialog.bind('<Control-s>', lambda e: save_content())  
        btn_frame = ttk.Frame(dialog)
        btn_frame.pack(pady=5)
    
        ttk.Button(btn_frame, text="保存", command=save_content).pack(side=tk.LEFT, padx=5)
        ttk.Button(btn_frame, text="关闭", command=dialog.destroy).pack(side=tk.RIGHT, padx=5)

    def on_tree_click(self, event):
        """处理左键点击事件"""
        item = self.tree.identify_row(event.y)
        if not item:
            return
            
        self.tree.selection_set(item)

    def on_tree_click(self, event):
        """处理点击事件键点击事件"""
        item = self.tree.identify_row(event.y)
        if item and event.num == 3:
            self.show_context_menu(event)
    def add_to_favorites(self, stock_code):
        """添加到自选股票"""
        self.favorite_stocks.add(stock_code)
        self.save_favorites()
        self.log(f"已将 {stock_code} 加入自选", "success")

    def remove_from_favorites(self, stock_code):
        """从自选股票中删除"""
        if stock_code in self.favorite_stocks:
            self.favorite_stocks.remove(stock_code)
            self.save_favorites()
            self.log(f"已将 {stock_code} 从自选中删除", "success")

    def show_favorites(self, event=None):
        """显示自选股票"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
            
        self.add_to_history(self.current_df.copy())
        mask = self.original_df['代码'].isin(self.favorite_stocks)
        filtered_df = self.original_df[mask].copy()
        self.current_df = filtered_df
        self.show_data()
        
        # 显示自选股票信息
        if filtered_df.empty:
            if not self.favorite_stocks:
                self.log("当前没有自选股票", "info")
            else:
                self.log(f"当前有 {len(self.favorite_stocks)} 个自选股票，但在当前数据中未找到", "info")
                self.log(f"自选股票列表: {', '.join(sorted(self.favorite_stocks))}", "info")
        else:
            self.log(f"显示自选股票，共找到 {len(filtered_df)} 条", "info")
            self.log("按Ctrl+Z可以回退到上一次显示状态", "info")

    def copy_selected_row(self, event=None):
        try:
            selected_items = self.tree.selection()
            if not selected_items:
                return

            # 获取DataFrame实际列名
            df_columns = self.current_df.columns.tolist()
            required_columns = ["代码", "名称", "最新价", "涨跌幅", 
                              "成交量", "成交额", "换手率", "量比", "市盈率-动态", "市净率","总市值"]
            
            rows_data = []
            for item in selected_items:
                display_index = int(self.tree.item(item, 'values')[0]) - 1
                if 0 <= display_index < len(self.current_df):
                    row = self.current_df.iloc[display_index].copy()
                    
                    # 格式化数值字段
                    numeric_cols = ['最新价', '涨跌幅', '成交量', '成交额', 
                                  '换手率', '量比', '市盈率-动态', "市净率",'总市值']
                    for col in numeric_cols:
                        if col in row:
                            value = float(row[col])
                            row[col] = f"{value:.2f}".replace(',', '')
                    
                    row_values = [str(row[col]) for col in required_columns]
                    rows_data.append("\t".join(row_values))

            header = "\t".join(required_columns)
            clipboard_data = header + "\n" + "\n".join(rows_data)
            
            self.master.clipboard_clear()
            self.master.clipboard_append(clipboard_data)
            self.log(f"已复制 {len(rows_data)} 行数据到剪贴板", "success")
            
        except Exception as e:
            self.log(f"复制失败: {str(e)}", "error")

    def copy_selected_names(self, event=None):
        """复制选中行的名称"""
        try:
            selected_items = self.tree.selection()
            if not selected_items:
                self.log("没有选中行","warning")
                return
            names = []
            for item in selected_items:
                item_values = self.tree.item(item, 'values')
                if len(item_values) >= 3:
                    name = item_values[2]
                    names.append(name)
            if not names:
                self.log("选中行中没有名称","warning")
                return  
            clipboard_text = "\n".join(names)
            self.master.clipboard_clear()
            self.master.clipboard_append(clipboard_text)
            self.log(f"已复制 {len(names)} 个名称到剪贴板: {clipboard_text}", "success")
        except Exception as e:
            self.log(f"复制名称失败: {str(e)}", "error")
    def select_all_rows(self, event=None):
        """全选Treeview中的所有行"""
        try:
            items = self.tree.get_children()
            self.tree.selection_set(items)
            self.log(f"已全选 {len(items)} 行", "info")
        except Exception as e:
            self.log(f"全选失败: {str(e)}", "error")

    def show_help(self, event=None):
        """显示专业排版的帮助信息"""
        if self.help_dialog and self.help_dialog.winfo_exists():
            self.help_dialog.lift()
            return

        # 创建对话框窗口
        self.help_dialog = tk.Toplevel(self.master)
        self.help_dialog.title("")
        self.help_dialog.geometry("680x520")
        self.help_dialog.resizable(False, False)

        # ==================== 滚动区域设置 ====================
        canvas = tk.Canvas(self.help_dialog, borderwidth=0)
        scrollbar = ttk.Scrollbar(self.help_dialog, orient="vertical", command=canvas.yview)
        scrollable_frame = ttk.Frame(canvas)

        # 配置画布滚动
        canvas.configure(yscrollcommand=scrollbar.set)
        canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        canvas.bind("<Configure>", lambda e: canvas.configure(scrollregion=canvas.bbox("all")))

        # 布局组件
        canvas.pack(side="left", fill="both", expand=True)
        scrollbar.pack(side="right", fill="y")

        # ==================== 内容容器 ====================
        content_frame = ttk.Frame(scrollable_frame)
        content_frame.pack(padx=20, pady=20, fill=tk.BOTH, expand=True)

        # ==================== 样式配置 ====================
        style = ttk.Style()
        style.configure("HelpTitle.TLabel", 
                       font=('Microsoft YaHei', 14, 'bold'),
                       foreground="#2C3E50",
                       padding=(0, 15, 0, 5))
        
        style.configure("Category.TLabel",
                       font=('Microsoft YaHei', 12, 'bold'),
                       foreground="#34495E",
                       padding=(0, 10, 0, 5))
        
        style.configure("Shortcut.TLabel",
                       font=('Consolas', 11),
                       foreground="#2C3E50",
                       padding=(10, 2, 10, 2))

        # ==================== 帮助内容 ====================
        sections = [
            ("全局操作", [
                ("Ctrl + O", "打开Excel文件"),
                ("Ctrl + R", "刷新实时数据"),
                ("F11", "全屏模式切换"),
                ("ESC", "退出全屏"),
                ("Ctrl + H", "显示帮助信息")
            ]),
            
            ("数据操作", [
                ("Ctrl + S", "保存当前视图数据"),
                ("Ctrl + F", "打开搜索对话框"),
                ("Ctrl + Z", "撤销操作（最多50步历史）"),
                ("Ctrl + A", "全选所有行"),
                ("Ctrl + C", "复制选中行完整数据"),
                ("Ctrl + D", "复制选中股票名称")
            ]),
            
            ("股票筛选", [
                ("单击筛选按钮", "启用/禁用该筛选条件"),
                ("双击筛选按钮", "设置筛选参数范围"),
                ("Ctrl + I", "显示自选股票列表"),
                ("Ctrl + B", "显示标记股票列表")
            ]),
            
            ("右键菜单", [
                ("标记/取消标记", "红色高亮重点股票"),
                ("加入自选股", "管理个人股票池"),
                ("添加备注", "记录个股分析笔记"),
                ("双击股票行", "浏览器查看最新行情")
            ]),
            
            ("浏览操作", [
                ("↑/↓", "行间导航"),
            ])
        ]

        # ==================== 构建界面元素 ====================
        # 主标题
        ttk.Label(content_frame, 
                 text="鹰眼股票分析系统 快捷键手册",
                 style="HelpTitle.TLabel").grid(row=0, column=0, sticky=tk.W)

        # 动态生成内容
        row_index = 1
        for section in sections:
            # 分类标题
            ttk.Label(content_frame, 
                     text=section[0], 
                     style="Category.TLabel").grid(row=row_index, column=0, sticky=tk.W)
            row_index += 1
            
            # 分隔线
            sep = ttk.Separator(content_frame, orient=tk.HORIZONTAL)
            sep.grid(row=row_index, column=0, sticky=tk.EW, pady=5)
            row_index += 1
            
            # 快捷键条目
            for shortcut, desc in section[1]:
                frame = ttk.Frame(content_frame)
                frame.grid(row=row_index, column=0, sticky=tk.W, pady=2)
                
                # 快捷键样式
                ttk.Label(frame, 
                          text=shortcut, 
                          style="Shortcut.TLabel",
                          background="#ECF0F1",
                          borderwidth=1,
                          relief="solid").pack(side=tk.LEFT)
                
                # 功能描述
                ttk.Label(frame, 
                          text=desc,
                          font=('Microsoft YaHei', 11),
                          foreground="#7F8C8D").pack(side=tk.LEFT, padx=10)
                
                row_index += 1

        # 版本信息
        ttk.Label(content_frame, 
                 text="Version 1.2 | © 2025 鹰眼分析系统",
                 font=('Microsoft YaHei', 9),
                 foreground="#95A5A6").grid(row=row_index+1, column=0, pady=15)

        # ==================== 事件绑定 ====================
        def _on_mousewheel(event):
            """处理鼠标滚轮事件"""
            canvas.yview_scroll(int(-1*(event.delta/120)), "units")
        
        canvas.bind_all("<MouseWheel>", _on_mousewheel)
        self.help_dialog.protocol("WM_DELETE_WINDOW", lambda: self.help_dialog.destroy())
        
    def show_statistics(self):
        """显示统计信息"""
        if self.current_df is None or self.current_df.empty:
            return

        try:
            # 计算总总市值（转换为万亿元）
            total_market_cap = self.current_df['总市值'].sum() / 1e12
            # 计算总成交额（转换为亿元）
            total_amount = self.current_df['成交额'].sum() / 1e8
            # 计算涨跌家数
            up_count = len(self.current_df[self.current_df['涨跌幅'] > 0])
            flat_count = len(self.current_df[self.current_df['涨跌幅'] == 0])
            down_count = len(self.current_df[self.current_df['涨跌幅'] < 0])
        
            # 清空日志并显示统计信息
            self.log_area.configure(state=tk.NORMAL)
            self.log_area.delete(1.0, tk.END)
        
            # 总市值
            self.log_area.insert(tk.END, f"总市值: {total_market_cap:.2f} 万亿元    ", "stat")
            # 总成交额
            self.log_area.insert(tk.END, f"总成交额: {total_amount:.2f} 亿元    ", "stat")
        
            # 涨跌家数
            self.log_area.insert(tk.END, "", "stat")
            self.log_area.insert(tk.END, f"{up_count}", "up")
            self.log_area.insert(tk.END, ":","stat")
            self.log_area.insert(tk.END, f"{flat_count}", "flat")
            self.log_area.insert(tk.END, ":","stat")
            self.log_area.insert(tk.END, f"{down_count}    ", "down")
        
            self.log_area.configure(state=tk.DISABLED)
        
        except KeyError as e:
            self.log(f"缺少必要列 {str(e)}，无法计算统计信息", "error")
        except Exception as e:
            self.log(f"统计计算失败: {str(e)}", "error")
    def add_to_history(self, df):
        """添加数据到历史记录"""
        if df is None or df.empty:
            return
        df_copy = df.copy(deep=True)
    
        MAX_HISTORY = 50
        if len(self.data_history) >= MAX_HISTORY:
            self.data_history = self.data_history[-(MAX_HISTORY-1):]
    
        # 如果当前不是在历史记录的最后，删除当前位置之后的记录
        if self.current_data_index < len(self.data_history) - 1:
            self.data_history = self.data_history[:self.current_data_index + 1]
    
        # 添加新的数据到历史记录
        self.data_history.append(df.copy())
        self.current_data_index = len(self.data_history) - 1

    def undo_last_display(self, event=None):
        """撤销上一次显示操作，最多支持50步回退"""
        if self.current_data_index > 0:
            self.current_data_index -= 1
            self.current_df = (
                self.data_history[self.current_data_index].copy()
                if self.data_history[self.current_data_index] is not None
                else None
            )
            self.show_data()
            self.log(f"已回退到第{len(self.data_history)-self.current_data_index}步历史记录", "info")
        else:
            self.log("已经是最早的历史记录", "warning")

    def on_amount_click(self, event):
        """处理成交额按钮单击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        self._after_id = self.master.after(300, self.filter_by_amount)

    def on_amount_double_click(self, event):
        """处理成交额按钮双击事件"""
        if hasattr(self, '_after_id'):
            self.master.after_cancel(self._after_id)
            del self._after_id
            
        dialog = tk.Toplevel(self.master)
        dialog.title("设置成交额区间")
        dialog.geometry("300x120")
        ttk.Label(dialog, text="输入成交额区间（万元，例如：5000-50000）:").pack(pady=5)
        entry = ttk.Entry(dialog)
        entry.insert(0, f"{self.current_amount_range[0]}-{self.current_amount_range[1]}")
        entry.pack(pady=5)
        entry.focus_set()
        
        def save_range():
            input_val = entry.get().strip()
            try:
                min_val, max_val = map(float, input_val.split('-'))
                if min_val > max_val:
                    min_val, max_val = max_val, min_val
                self.current_amount_range = (min_val, max_val)
                self.amount_btn.config(text=f"成交额{min_val}-{max_val}万")
                self.save_settings()
                dialog.destroy()
            except:
                messagebox.showerror("错误", "输入格式错误，示例：5000-50000")
        
        entry.bind('<Return>', lambda e: save_range())
        ttk.Button(dialog, text="保存", command=save_range).pack()
        
    def show_marked_stocks(self, event=None):
        """显示所有标记的股票"""
        if self.original_df is None or self.original_df.empty:
            messagebox.showwarning("警告", "请先加载数据")
            return
        self.add_to_history(self.current_df.copy())
      
        mask = self.original_df['代码'].isin(self.marked_stocks)
        filtered_df = self.original_df[mask].copy()
        self.current_df = filtered_df
        self.show_data()
    
        # 显示标记股票信息
        if filtered_df.empty:
            if not self.marked_stocks:
                self.log("当前没有标记的股票", "info")
            else:
                marked_list = "\n".join(sorted(self.marked_stocks))
                self.log(f"当前有 {len(self.marked_stocks)} 个标记股票，但在当前数据中未找到", "info")
                self.log(f"标记股票列表:\n{marked_list}", "info")
        else:
            self.log(f"显示标记股票，共找到 {len(filtered_df)}", "info")
            self.log("按Ctrl+Z可以回退到上一次显示状态", "info")

if __name__ == "__main__":
    if sys.platform == 'win32':
        from ctypes import windll
        windll.kernel32.SetConsoleOutputCP(65001)
    root = tk.Tk()
    try:
        from ctypes import windll
        windll.shcore.SetProcessDpiAwareness(1)
    except: pass
    app = ExcelBrowser(root)

    root.mainloop()
